<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>作用域和作用域链</title>
</head>

<body>

</body>

</html>
<script>
  // 作用域和作用域链
  // 我们在调用某个变量的时候，会顺着作用域空间去寻找，直到找到全局为止


  // 原型和原型链 找父类
  // 面向对象之间的 在对象之间
  // 定义一个对象 和 定义一个函数
  // 对象里面只会有两样东西：属性、方法


  //   一、构造函数
  // 构造函数就是一个普通的函数，创建方式和普通函数没有区别，不同的是构造函数习惯上首字母大写。
  // 另外就是调用方式的不同，普通函数是直接调用，而构造函数需要使用new关键字来调用。
  // function -> new Object()
  function Person(nickname, age, money) {
    //定义属性
    this.nickname = nickname
    this.age = age
    this.money = money

    //方法
    this.work = function () {
      this.money += 1000
      return `${this.nickname}正在工作`
    }

  }


  //   二、原型
  // 每当定义一个函数数据类型(普通函数、类)时候，都会天生自带一个prototype属性，
  // 这个属性指向函数的原型对象，并且这个属性是一个对象数据类型的值。
  console.log(Person.prototype);
  // 通过原型来对Person对象进行拓展
  // Person.prototype.sleep = function()
  // {
  //   return `${this.nickname} 正在睡觉`
  // }

  //   三、原型链
  // 1.__proto__和constructor
  // 每一个对象数据类型(普通的对象、实例、prototype......)也天生自带一个属性__proto__，
  // 属性值是当前实例所属类的原型(prototype)。
  // 原型对象中有一个属性constructor, 它指向函数对象。

  //   __proto__属性是prototye的容器。
  // 在官方的es5中，定义了一个名叫[[prototype]]的属性，
  // 每个对象都拥有这样一个属性，这个属性是一个指针，它指向一个名叫原型对象的内存堆。
  // 而原型对象也是对象，因此又含有自己的[[prototype]]的属性，又指向下一个原型对象。
  // 那么终点是哪? 当然是我们的Object.prototype对象。
  // __proto__属性是什么  https://blog.csdn.net/weixin_37150764/article/details/109113206


  // Person() 调用函数
  //调用对象 new 实例化
  var obj = new Person('张三', 20, 0)
  //打印对象 实际上是输出对象的toString()方法的返回值
  console.log(obj);
  //调用属性
  console.log(obj.nickname)
  //调用方法
  console.log(obj.work())
  // console.log(obj.sleep())


  // __proto__ 私有属性 用来找对象的父类 指向之类的一个属性
  // prototype 这个属性 是用于给某个对象进行 继承拓展用处
  // 原型链：用来找父类，找到空null的情况下就说明找到头了 所形成的这条链就是原型链
  // 层级：obj -> Person -> Object -> null 原型链
  // 父子关系： null -> Obiect -> Person -> obj
  console.log(obj) //new person
  console.log(obj.__proto__) //function person
  console.log(obj.__proto__.__proto__) //JS底层Object对象 new Object
  console.log(obj.__proto__.__proto__.__proto__) //null



  //   proto、prototype、constructor 的关系
  // 示例：
  var o = {};
  // // true ：对象的__proto__等于类的prototype
  console.log(o.__proto__ === Object.prototype);
  // // true ：对象的constructor 等于 类
  console.log(o.constructor === Object);
  // // true ：o.constructor.prototype 可表示对象的原型。
  console.log(o.constructor.prototype === Object.prototype);



  // Object 对象方法 合并对象Object.assign（对象a，对象b，对象c）
  var a = { nickname: '张三' }
  var b = { age: 20 }
  var c = { sex: '男' }
  // 合并对象 返回合并后的结果
  var res = Object.assign(a, b, c)
  console.log(res)
  // 合并具有相同属性的对象 （后面对象的相同属性的值 会覆盖掉 前面对象对应的属性的值）
  const o1 = { a: 1, b: 1, c: 1 };
  const o2 = { b: 2, c: 2 };
  const o3 = { c: 3 };
  const objAA = Object.assign({}, o1, o2, o3);
  console.log(objAA); // { a: 1, b: 2, c: 3 }
  //数组的处理 （以数组下标为基准进行合并替换，[2,3] [5] =>  [0]=2 [0]=5  =>[5,3]）
  console.log(Object.assign([2, 3], [5]));  // [5,3]




  // Object.is(value1, value2) 判断值是否相等

  //判断值是否相等
  console.log(Object.is('q', 'q')) //true
  //与（===）的区别
  //一是+0不等于-0
  console.log(Object.is(+0, -0));  //false
  console.log(+0 === -0)  //true
  //二是NaN等于本身
  console.log(Object.is(NaN, NaN)); //true
  console.log(NaN === NaN)  //false
  //申请变量的时候，变量会分配内存 内存地址不一样 那么两个变量就不一样
  console.log(Object.is([1], [1])) //false



  // Object.defineProperty() 直接在一个对象上定义一个新属性，或者修改一个对象的现有属性，并返回此对象。

  //给某个对象增添新的属性
  // obj.work = '工作技能'
  //在定义一个自定义属性的时候 work _work
  // work == _work  自动捆绑在一起的
  Object.defineProperty(res, 'work', {
    get: function () {
      return this._work || '工作技能'
    },
    set: function (value) {
      // console.log(this)
      // this.work = value
      this._work = value
    }
  })
  //调用打印的时候，实际上调用就是get方法
  console.log(res.work)
  //修改属性实际上调用的是 set方法
  res.work = '修改这个属性的值'
  console.log(res.work)



  // Object.keys(), Object.values()
  // Object.keys(): 返回一个由一个给定对象的自身可枚举属性组成的数组，数组中属性名的排列顺序和正常循环遍历该对象时返回的顺序一致 。
  // Object.values(): 返回一个给定对象自身的所有可枚举属性值的数组.

  // nickname age sex work
  //Object.keys -> 提取出对象的所有属性
  console.log(Object.keys(res))
  // Object.values -> 提取出对象的所有的值
  console.log(Object.values(res))

  // 简单数组
  var arr = ['a', 'b', 'c'];
  console.log(Object.keys(arr)); // console: ['0', '1', '2'];
  console.log(Object.values(obj)); // ['bar', 42]

  // 类数组对象
  var obj = { 0: 'a', 1: 'b', 2: 'c' };
  console.log(Object.keys(obj)); // console: ['0', '1', '2']
  console.log(Object.values(obj)); // ['a', 'b', 'c']

  // 排序的类数组对象
  var anObj = { 100: 'a', 2: 'b', 7: 'c' };
  console.log(Object.keys(anObj)); // console: ['2', '7', '100']
  console.log(Object.values(anObj)); // ['b', 'c', 'a']
</script>